﻿/**
 *	\file		quaternion.hpp
 *	\date		(2011-07-09 11:26:32)/(2015-02-01 23:55:26)
 *-----------------------------------------------------------------------------
 *	\brief		四元数.
 *	\version	1.0.1.3
 *	\author		Nick Shallery	(nicknide@gmail.com)
 *	\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/

#if			!defined(__LOLIX__SOLA__QUATERNION__NICK__K_IN_G__)
#define		__LOLIX__SOLA__QUATERNION__NICK__K_IN_G__

#if			!defined(__LOLIX__LOLI_X_MACRO__K_IN_G__)
	#include	"../lolix_macro.h"
#endif	//	!defined(__LOLIX__LOLI_X_MACRO__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_TYPE__K_IN_G__)
	#include	"../lolix_type.h"
#endif	//	!defined(__LOLIX__LOLIX_TYPE__K_IN_G__)

#if			!defined(__LOLIX__SOLA__VEC__NICK__K_IN_G__)
	#include	"./vec.hpp"
#endif	//	!defined(__LOLIX__SOLA__VEC__NICK__K_IN_G__)

namespace lolix{namespace sola{
	/**
	* \brief	四元数.
	* \tparam _Ty	四元数的数值类型.
	**/
	template<typename _Ty = lolix::lx_f32>
	struct quaternion
	{
		typedef quaternion<_Ty> this_type;
		typedef vec<3, _Ty> vector_type;;
		typedef _Ty scalar_type;
		vector_type v;
		scalar_type s;

		LOLIX_INLINE_CALL quaternion(){}
		LOLIX_INLINE_CALL quaternion(_Ty s_, _Ty x_, _Ty y_, _Ty z_):s(s_), v(x_, y_, z_){}
		LOLIX_INLINE_CALL quaternion(scalar_type s_, vector_type const& v_):s(s_), v(v_){}
		LOLIX_INLINE_CALL bool operator == (this_type const& o)const
			{
			return this->s == o.s && this->v == o.v;
			}

		LOLIX_INLINE_CALL bool operator != (this_type const& o)const
			{
			return !(this->operator == (o));
			}

		LOLIX_INLINE_CALL this_type& operator *= (this_type const& o)
			{
			_Ty vs = this->s*o.s - this->v.dot(o.v);
			//_Ty vs = this->s*o.s - this->v.x*o.v.x - this->v.y*o.v.y - this->v.z*o.v.z;
			_Ty vx = this->v.x*o.s + this->s*o.v.x + this->v.y*o.v.z - this->v.z*o.v.y;
			_Ty vy = this->v.y*o.s + this->s*o.v.y + this->v.z*o.v.x - this->v.x*o.v.z;
			_Ty vz = this->v.z*o.s + this->s*o.v.z + this->v.x*o.v.y - this->v.y*o.v.x;
			s = vs;
			this->v.x = vx;
			this->v.y = vy;
			this->v.z = vz;
			return *this;
			}

		template<typename _TyMul>
		LOLIX_INLINE_CALL this_type& operator *= (_TyMul val)
			{
			this->s *= val;
			this->v *= val;
			return *this;
			}

		template<typename _To>
		LOLIX_INLINE_CALL this_type& operator *= (vec<3, _To> const& v)
			{
			_Ty vs = -this->v.dot(v);
			_Ty vx = this->s*v.x + this->v.y*v.z - this->v.z*v.y;
			_Ty vy = this->s*v.y + this->v.z*v.x - this->v.x*v.z;
			_Ty vz = this->s*v.z + this->v.x*v.y - this->v.y*v.x;
			s = vs;
			this->v.x = vx;
			this->v.y = vy;
			this->v.z = vz;
			return *this;
			}

		LOLIX_INLINE_CALL this_type& operator += (this_type const& o)
			{
			this->s += o.s;
			this->v += o.v;
			return *this;
			}

		LOLIX_INLINE_CALL this_type& operator -= (this_type const& o)
			{
			this->s -= o.s;
			this->v -= o.v;
			return *this;
			}

		LOLIX_INLINE_CALL _Ty dot(this_type const& o)const
			{
			return this->s * o.s + this->v.dot(o.v);
			}


		LOLIX_INLINE_CALL this_type conjugate(void)const
			{
			return this_type(this->s, -this->v);
			}

		LOLIX_INLINE_CALL this_type& conjugate_self(void)
			{
			this->v.x = -this->v.x;
			this->v.y = -this->v.y;
			this->v.z = -this->v.z;
			return *this;
			}

		LOLIX_INLINE_CALL this_type inverse(void)const
			{
			_Ty d = static_cast<_Ty>(1)/this->dot(*this);
			return this_type(this->s*d, this->v*(d*static_cast<_Ty>(-1)));
			}

		LOLIX_INLINE_CALL this_type& inverse_self(void)
			{
			_Ty d = static_cast<_Ty>(1)/this->dot(*this);
			this->s *= d;
			d *= static_cast<_Ty>(-1);
			this->v.x *= d;
			this->v.y *= d;
			this->v.z *= d;
			return *this;
			}

		LOLIX_INLINE_CALL _Ty size_p2(void)const
			{
			return this->s * this->s + this->v.size_p2();
			}

		template<typename _Ti>
		LOLIX_INLINE_CALL vec<3, _Ti> route(vec<3, _Ti> const& v)
			{
			this_type t(*this);
			t *= v;
			t *= this->conjugate();
			return t.v;
			}
	};

	template<typename _Tval, typename _Tmul>
	LOLIX_INLINE_CALL quaternion<_Tval> operator * (quaternion<_Tval> const& q, _Tmul v)
	{
		quaternion<_Tval> tmp(q);
		tmp *= v;
		return tmp;
	}

	template<typename _Tval>
	LOLIX_INLINE_CALL quaternion<_Tval> operator + (quaternion<_Tval> const& l, quaternion<_Tval> const& r)
	{
		quaternion<_Tval> t(l);
		t.s += r.s;
		t.v += r.v;
		return t;
	}

	typedef quaternion<float> quat;
}}

#endif	//	!defined(__LOLIX__SOLA__QUATERNION__NICK__K_IN_G__)
